#pragma region Licensing
/*
       LOGL::GLib -> LorgonJortle's OpenGL Game Library
       Copyright (C) 2011  Jesse 'LorgonJortle' Wilkerson

       This program is free software: you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation, either version 3 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#pragma endregion

#include "Vector3.h"

using namespace LOGL_GLib::Math;

namespace LOGL_GLib
{
     namespace Common
     {
          /******************************************************************************
           Vector3::Vector3();
                    Arguments:
                         void

                    Description:
                         Constructs a Vector3 object with all components set to zero.

                    Returns:
                         The Vector3 object.
          *******************************************************************************/
          Vector3::Vector3()
          {
               X = 0.0f;
               Y = 0.0f;
               Z = 0.0f;
          }

          /******************************************************************************
           Vector3::Vector3(GLfloat _XYZ);
                    Arguments:
                         GLfloat _XYZ:
                              The value to set the X, Y, and Z components to.

                    Description:
                         Constructs a Vector3 object with all components set to the specified value.

                    Returns:
                         The Vector3 object.
          *******************************************************************************/
          Vector3::Vector3(GLfloat _XYZ)
          {
               X = _XYZ;
               Y = _XYZ;
               Z = _XYZ;
          }

          /******************************************************************************
           Vector3::Vector3(GLfloat _X, GLfloat _Y, GLfloat _Z);
                    Arguments:
                         GLfloat _X:
                              The value to set the X component to.
                         GLfloat _Y:
                              The value to set the Y component to.
                         GLfloat _Z:
                              The value to set the Z component to.

                    Description:
                         Constructs a Vector3 object with the specified values.

                    Returns:
                         The Vector3 object.
          *******************************************************************************/
          Vector3::Vector3(GLfloat _X, GLfloat _Y, GLfloat _Z)
          {
               X = _X;
               Y = _Y;
               Z = _Z;
          }

          /******************************************************************************
           GLboolean Vector3::operator==(const Vector3& _Vector) const;
                    Arguments:
                         const Vector3& _Vector;
                              The vector to compare against.

                    Description:
                         Determines if the specified vector is equal to this.

                    Returns:
                         Whether or not the two vectors are equal.
          *******************************************************************************/
          GLboolean  Vector3::operator==(const Vector3& _Vector) const
          {
               if(Equal(X, _Vector.X) && Equal(Y, _Vector.Y) && Equal(Z, _Vector.Z))
                    return GL_TRUE;
               else
                    return GL_FALSE;
          }

          /******************************************************************************
           GLboolean Vector3::operator!=(const Vector3& _Vector) const;
                    Arguments:
                         const Vector3& _Vector;
                              The vector to compare against.

                    Description:
                         Determines if the specified vector is not equal to this.

                    Returns:
                         Whether or not the two vectors are not equal.
          *******************************************************************************/
          GLboolean Vector3::operator!=(const Vector3& _Vector) const
          {
               return !(*this == _Vector);
          }

          /******************************************************************************
           Vector3& Vector3::operator=(const Vector3& _Vector);
                    Arguments:
                         const Vector3& _Vector;
                              The vector to assign to this.

                    Description:
                         Assigns the specified vector to this.

                    Returns:
                         A reference to this, for daisy chaining.
          *******************************************************************************/
          Vector3& Vector3::operator=(const Vector3& _Vector)
          {
               X = _Vector.X;
               Y = _Vector.Y;
               Z = _Vector.Z;

               return *this;
          }

          /******************************************************************************
           Vector3& Vector3::operator=(GLfloat _XYZ);
                    Arguments:
                         GLfloat _XYZ:
                              The value to assign each component.

                    Description:
                         Assigns the specified value to all components of this.

                    Returns:
                         A reference to this, for daisy chaining.
          *******************************************************************************/
          Vector3& Vector3::operator=(GLfloat _XYZ)
          {
               X = _XYZ;
               Y = _XYZ;
               Z = _XYZ;

               return *this;
          }

          /******************************************************************************
           Vector3 Vector3::operator-() const;
                    Arguments:
                         void

                    Description:
                         Computes the negated version of this.

                    Returns:
                         The negated version of this.
          *******************************************************************************/
          Vector3 Vector3::operator-() const
          {
               return Vector3(-X, -Y, -Z);
          }

          /******************************************************************************
           Vector3 Vector3::operator +(const Vector3& _Delta) const;
                    Arguments:
                         const Vector3& _Delta:
                              The vector to add to another.

                    Description:
                         Computes the sum of two vectors.

                    Returns:
                         The resultant vector.
          *******************************************************************************/
          Vector3 Vector3::operator +(const Vector3& _Delta) const
          {
               Vector3 resultant;

               resultant.X = X + _Delta.X;
               resultant.Y = Y + _Delta.Y;
               resultant.Z = Z + _Delta.Z;

               return resultant;
          }

          /******************************************************************************
           GLvoid Vector3::operator +=(const Vector3& _Delta);
                    Arguments:
                         const Vector3& _Delta:
                              The vector to add to this.

                    Description:
                         Adds the specified vector to this.

                    Returns:
                         void
          *******************************************************************************/
          GLvoid Vector3::operator +=(const Vector3& _Delta)
          {
               X += _Delta.X;
               Y += _Delta.Y;
               Z += _Delta.Z;
          }

          /******************************************************************************
           Vector3 Vector3::operator -(const Vector3& _Delta) const;
                    Arguments:
                         const Vector3& _Delta:
                              The vector to subtract from another.

                    Description:
                         Computes the difference of two vectors.

                    Returns:
                         The resulting difference vector.
          *******************************************************************************/
          Vector3 Vector3::operator -(const Vector3& _Delta) const
          {
               Vector3 resultant;

               resultant.X = X - _Delta.X;
               resultant.Y = Y - _Delta.Y;
               resultant.Z = Z - _Delta.Z;

               return resultant;
          }

          /******************************************************************************
           GLvoid Vector3::operator -=(const Vector3& _Delta);
                    Arguments:
                         const Vector3& _Delta:
                              The vector to subtract from this.

                    Description:
                         Subtracts the specified vector from this.

                    Returns:
                         void
          *******************************************************************************/
          GLvoid Vector3::operator -=(const Vector3& _Delta)
          {
               X -= _Delta.X;
               Y -= _Delta.Y;
               Z -= _Delta.Z;
          }

          /******************************************************************************
           Vector3 Vector3::operator *(GLfloat _Scale) const;
                    Arguments:
                         GLfloat _Scale:
                              The scalar to multiply the vector by.

                    Description:
                         Computes the scaled resultant of a vector and scalar.

                    Returns:
                         The resulting scaled vector.
          *******************************************************************************/
          Vector3 Vector3::operator *(GLfloat _Scale) const
          {
               Vector3 resultant;

               resultant.X = X * _Scale;
               resultant.Y = Y * _Scale;
               resultant.Z = Z * _Scale;

               return resultant;
          }

          /******************************************************************************
           GLvoid Vector3::operator *=(GLfloat _Scale);
                    Arguments:
                         GLfloat _Scale:
                              The scalar to multiply this by.

                    Description:
                         Scales this by the specified scalar.

                    Returns:
                         void
          *******************************************************************************/
          GLvoid Vector3::operator *=(GLfloat _Scale)
          {
               X *= _Scale;
               Y *= _Scale;
               Z *= _Scale;
          }

          /******************************************************************************
           Vector3 Vector3::operator /(GLfloat _Divisor) const;
                    Arguments:
                         GLfloat _Divisor:
                              The scalar to divide the vector by.

                    Description:
                         Computes the resultant of this dived by the specified scalar.

                    Returns:
                         The resulting vector.
          *******************************************************************************/
          Vector3 Vector3::operator/(GLfloat _Divisor) const
          {
               Vector3 resultant;

               resultant.X = X / _Divisor;
               resultant.Y = Y / _Divisor;
               resultant.Z = Z / _Divisor;

               return resultant;
          }

          /******************************************************************************
           GLvoid Vector3::operator /=(GLfloat _Divisor) const;
                    Arguments:
                         GLfloat _Divisor:
                              The scalar to divide the vector by.

                    Description:
                         Divides this by the specified scalar.

                    Returns:
                         void
          *******************************************************************************/
          GLvoid Vector3::operator/=(GLfloat _Divisor)
          {
               X /= _Divisor;
               Y /= _Divisor;
               Z /= _Divisor;
          }

          /******************************************************************************
           std::ostream& Vector3::operator <<(std::ostream& _Stream,
                                              const Vector3& _Vector);
                    Arguments:
                         std::ostream& _Stream:
                              The ouput stream to write to.
                         const Vector3& _Vector:
                              The vector to write to the stream.

                    Description:
                         Outputs the specifed vector to the stream.

                    Returns:
                         A reference to the stream for daisy-chaining.
          *******************************************************************************/
          std::ostream& operator<<(std::ostream& _Stream, const Vector3& _Vector)
          {
               _Stream << _Vector.ToString();

               return _Stream;
          }

          /******************************************************************************
           GLvoid Vector3::Negate();
                    Arguments:
                         void

                    Description:
                         Negates all components of this.

                    Returns:
                         void
          *******************************************************************************/
          GLvoid Vector3::Negate()
          {
               X *= -1;
               Y *= -1;
               Z *= -1;
          }

          /******************************************************************************
           static Vector3 Vector3::Negate(const Vector3& _Vector);
                    Arguments:
                         const Vector3& _Vector:
                              The vector to negate.

                    Description:
                         Negates all components of the specified vector.

                    Returns:
                         The negated vector.
          *******************************************************************************/
          Vector3 Vector3::Negate(const Vector3& _Vector)
          {
               return Vector3(_Vector.X * -1.0f, _Vector.Y * -1.0f, _Vector.Z * -1.0f);
          }

          /******************************************************************************
           GLfloat Vector3::Length() const;
                    Arguments:
                         void

                    Description:
                         Calculates the length of the vector.

                    Returns:
                         The length of the vector.
          *******************************************************************************/
          GLfloat Vector3::Length() const
          {
               return (GLfloat)sqrt((X * X) + (Y * Y) + (Z * Z));
          }

          /******************************************************************************
           GLvoid Vector3::Normalize();
                    Arguments:
                         void

                    Description:
                         Calculates the appropriate unit vector.

                    Returns:
                         void
          *******************************************************************************/
          GLvoid Vector3::Normalize()
          {
               GLfloat length = Length();

               /* Avoid division by 0. */
               if(Equal(length, 0.0f))
                    length = 1.0f;

               X /= length;
               Y /= length;
               Z /= length;
          }

          /******************************************************************************
           static Vector3 Vector3::Normalize(const Vector3& _Vector);
                    Arguments:
                         const Vector3& _Vector:
                              The vector to return the normal of.

                    Description:
                         Calculates the appropriate unit vector for the specified vector.

                    Returns:
                         The appropriate unit vector for the specified vector.
          *******************************************************************************/
          Vector3 Vector3::Normalize(const Vector3& _Vector)
          {
               GLfloat length = _Vector.Length();

               /* Avoid division by zero. */
               if(Equal(length, 0.0f))
                    length = 1.0f;

               Vector3 norm = _Vector;

               norm.X /= length;
               norm.Y /= length;
               norm.Z /= length;

               return norm;
          }

          /******************************************************************************
           GLfloat Vector3::Dot(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The vector to calculate the dot product against.

                    Description:
                         Calculates the dot product between this and the specified vector.

                    Returns:
                         The dot product between this and the specified vector.
          *******************************************************************************/
          GLfloat Vector3::Dot(const Vector3& _B) const
          {
               return (X * _B.X) + (Y * _B.Y) + (Z * _B.Z);
          }

          /******************************************************************************
           static GLfloat Vector3::Dot(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Calculates the dot product between the two specified vectors.

                    Returns:
                         The dot product between the two specified vectors.
          *******************************************************************************/
          GLfloat Vector3::Dot(const Vector3& _A, const Vector3& _B)
          {
               return (_A.X * _B.X) + (_A.Y * _B.Y) + (_A.Z * _B.Z);
          }

          /******************************************************************************
           Vector3 Vector3::Cross(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The vector to calculate the cross product against.

                    Description:
                         Calculates the cross product between this and the specified vector.

                    Returns:
                         A vector perpendicular to this and the specified vector.
          *******************************************************************************/
          Vector3 Vector3::Cross(const Vector3& _B) const
          {
               Vector3 resultant;

               resultant.X = (Y * _B.Z) - (Z * _B.Y);
               resultant.Y = (Z * _B.X) - (X * _B.Z);
               resultant.Z = (X * _B.Y) - (Y * _B.X);

               return resultant;
          }

          /******************************************************************************
           static Vector3 Vector3::Cross(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Calculates the cross product between the two specified vectors.

                    Returns:
                         A vector perpendicular to the two specified vectors.
          *******************************************************************************/
          Vector3 Vector3::Cross(const Vector3& _A, const Vector3& _B)
          {
               Vector3 resultant;

               resultant.X = (_A.Y * _B.Z) - (_A.Z * _B.Y);
               resultant.Y = (_A.Z * _B.X) - (_A.X * _B.Z);
               resultant.Z = (_A.X * _B.Y) - (_A.Y * _B.X);

               return resultant;
          }

          /******************************************************************************
           GLfloat Vector3::AngleBetween(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The vector to work with.

                    Description:
                         Calculates the angle (in radians) between this and the specified vector.

                    Returns:
                         The angle between this and the specified vector (in radians).
          *******************************************************************************/
          GLfloat Vector3::AngleBetween(const Vector3& _B) const
          {
               /* Calculate a difference vector. */
               Vector3 c = (*this - _B);

               /* Find all magnitudes. */
               GLfloat aLength = Length();
               GLfloat bLength = _B.Length();
               GLfloat cLength = c.Length();

               /* Check to see if this or _B are zero vectors. */
               if(Equal(aLength, 0.0f) || Equal(bLength, 0.0f))
                    return 0;

               /* If the length of c is zero, the vectors are equal. */
               if(Equal(cLength, 0.0f))
                    return 0;

               /* Otherwise, calculate the angle between them. */
               return acosf(((aLength * aLength) + (bLength * bLength) - (cLength * cLength)) / (2 * aLength * bLength));
          }

          /******************************************************************************
           static GLfloat Vector3::AngleBetween(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Calculates the angle (in radians) between the two specified vectors.

                    Returns:
                         The angle between the two specified vectors (in radians).
          *******************************************************************************/
          GLfloat Vector3::AngleBetween(const Vector3& _A, const Vector3& _B)
          {
               /* Calculate a difference vector. */
               Vector3 c = (_A - _B);

               /* Find all magnitudes. */
               GLfloat aLength = _A.Length();
               GLfloat bLength = _B.Length();
               GLfloat cLength = c.Length();

               /* Check to see if this or _B are zero vectors. */
               if(Equal(aLength, 0.0f) || Equal(bLength, 0.0f))
                    return 0;

               /* If the length of c is zero, the vectors are equal. */
               if(Equal(cLength, 0.0f))
                    return 0;

               /* Otherwise, calculate the angle between them. */
               return acosf(((aLength * aLength) + (bLength * bLength) - (cLength * cLength)) / (2 * aLength * bLength));
          }

          /******************************************************************************
           GLfloat Vector3::Component(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The vector to work with.

                    Description:
                         Calculates the distance this points in the direction of the specified vector.

                    Returns:
                         The distance this points in the direction of the specified vector.
          *******************************************************************************/
          GLfloat Vector3::Component(const Vector3& _B) const
          {
               if(Equal(Length(), 0.0f) || Equal(_B.Length(), 0.0f))
                    return 0;
               else
                    return Dot(_B) / _B.Length();
          }

          /******************************************************************************
           static GLfloat Vector3::Component(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Calculates the distance one vector points in the direction of another.

                    Returns:
                         The distance one vector points in the direction of another.
          *******************************************************************************/
          GLfloat Vector3::Component(const Vector3& _A, const Vector3& _B)
          {
               if(Equal(_A.Length(), 0) || Equal(_B.Length(), 0))
                    return 0;
               else
                    return _A.Dot(_B) / _B.Length();
          }

          /******************************************************************************
           Vector3 Vector3::Project(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The vector to work with.

                    Description:
                         Calculates this projected on the specified vector.

                    Returns:
                         The vector of this projected on the specified vector.
          *******************************************************************************/
          Vector3 Vector3::Project(const Vector3& _B) const
          {
               if(Equal(Length(), 0) || Equal(_B.Length(), 0))
                    return Vector3(0);
               else
               {
                    Vector3 vec = Vector3::Normalize(_B);
                    GLfloat comp = Component(_B);
                    vec *= comp;

                    return vec;
               }
          }

          /******************************************************************************
           static Vector3 Vector3::Project(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Calculates one vector projected on another.

                    Returns:
                         The resulting, projected vector.
          *******************************************************************************/
          Vector3 Vector3::Project(const Vector3& _A, const Vector3& _B)
          {
               if(Equal(_A.Length(), 0) || Equal(_B.Length(), 0))
                    return Vector3(0);
               else
               {
                    Vector3 vec = Vector3::Normalize(_B);
                    GLfloat comp = _A.Component(_B);
                    vec *= comp;

                    return vec;
               }
          }

          /******************************************************************************
           Vector3 Vector3::Reflect(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The vector to work with.

                    Description:
                         Calculates the reflection of this across the specified vector.

                    Returns:
                         The reflection vector of this across the specified vector.
          *******************************************************************************/
          Vector3 Vector3::Reflect(const Vector3& _B) const
          {
               if(Equal(Dot(_B), 0.0f))
                    return -(*this);
               else
               {
                    Vector3 vec = Vector3::Normalize(_B);
                    vec *= Dot(vec) * 2.0f;

                    return -(*this - vec);
               }
          }

          /******************************************************************************
           static Vector3 Vector3::Reflect(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Calculates the reflection of one vector across another.

                    Returns:
                         The reflection vector of the two specified vectors.
          *******************************************************************************/
          Vector3 Vector3::Reflect(const Vector3& _A, const Vector3& _B)
          {
               if(Equal(_A.Dot(_B), 0.0f))
                    return -_A;
               else
               {
                    Vector3 vec = Vector3::Normalize(_B);
                    vec *= _A.Dot(vec) * 2.0f;

                    return -(_A - vec);
               }
          }

          /******************************************************************************
           Vector3 Vector3::Maximize(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The second (first is this) vector to work with.

                    Description:
                         Creates a vector from the highest values of this and the specified vector.

                    Returns:
                         A vector from the highest values of this and the specified vector.
          *******************************************************************************/
          Vector3 Vector3::Maximize(const Vector3& _B) const
          {
               Vector3 max;
               max.X = X > _B.X ? X : _B.X;
               max.Y = Y > _B.Y ? Y : _B.Y;
               max.Z = Z > _B.Z ? Z : _B.Z;

               return max;
          }

          /******************************************************************************
           static Vector3 Vector3::Maximize(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Creates a vector from the highest values of the two specified vectors.

                    Returns:
                         A vector from the highest values of the two specified vectors.
          *******************************************************************************/
          Vector3 Vector3::Maximize(const Vector3& _A, const Vector3& _B)
          {
               Vector3 max;
               max.X = _A.X > _B.X ? _A.X : _B.X;
               max.Y = _A.Y > _B.Y ? _A.Y : _B.Y;
               max.Z = _A.Z > _B.Z ? _A.Z : _B.Z;

               return max;
          }

          /******************************************************************************
           Vector3 Vector3::Minimize(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The second (first is this) vector to work with.

                    Description:
                         Creates a vector from the lowest values of this and the specified vector.

                    Returns:
                         A vector from the lowest values of this and the specified vector.
          *******************************************************************************/
          Vector3 Vector3::Minimize(const Vector3& _B) const
          {
               Vector3 min;
               min.X = X < _B.X ? X : _B.X;
               min.Y = Y < _B.Y ? Y : _B.Y;
               min.Z = Z < _B.Z ? Z : _B.Z;

               return min;
          }

          /******************************************************************************
           static Vector3 Vector3::Minimize(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Creates a vector from the lowest values of the two specified vectors.

                    Returns:
                         A vector from the lowest values of the two specified vectors.
          *******************************************************************************/
          Vector3 Vector3::Minimize(const Vector3& _A, const Vector3& _B)
          {
               Vector3 min;
               min.X = _A.X < _B.X ? _A.X : _B.X;
               min.Y = _A.Y < _B.Y ? _A.Y : _B.Y;
               min.Z = _A.Z < _B.Z ? _A.Z : _B.Z;

               return min;
          }

          /******************************************************************************
           Vector3 Vector3::Average(const Vector3& _B) const;
                    Arguments:
                         const Vector3& _B:
                              The second (first is this) vector to work with.

                    Description:
                         Creates a vector from the average values of this and the specified vector.

                    Returns:
                         A vector from the average values of this and the specified vector.
          *******************************************************************************/
          Vector3 Vector3::Average(const Vector3& _B) const
          {
               Vector3 average;
               average.X = (X + _B.X) / 2;
               average.Y = (Y + _B.Y) / 2;
               average.Z = (Z + _B.Z) / 2;

               return average;
          }

          /******************************************************************************
           static Vector3 Vector3::Average(const Vector3& _A, const Vector3& _B);
                    Arguments:
                         const Vector3& _A:
                              The first vector to work with.
                         const Vector3& _B:
                              The second vector to work with.

                    Description:
                         Creates a vector from the average values of the two specified vectors.

                    Returns:
                         A vector from the average values of the two specified vectors.
          *******************************************************************************/
          Vector3 Vector3::Average(const Vector3& _A, const Vector3& _B)
          {
               Vector3 average;
               average.X = (_A.X + _B.X) / 2;
               average.Y = (_A.Y + _B.Y) / 2;
               average.Z = (_A.Z + _B.Z) / 2;

               return average;
          }

          /******************************************************************************
           std::string Vector3::ToString() const;
                    Arguments:
                         void

                    Description:
                         Gets a string representation of the vector.

                    Returns:
                         A string representation of the vector.
          *******************************************************************************/
          std::string Vector3::ToString() const
          {
               std::stringstream stream;
               stream << "(" << X << "," << Y << "," << Z << ")";
               return stream.str();
          }

     } /* Namespace Common. */
} /* Namespace LOGL_GLib. */
